基础知识-着色器

基本概念

Shader

Shader (着色器):运行在 GPU 的小程序,自定义显卡渲染画面的算法,使画面达到我们想要的效果。本质就是一段代码(主流的有 基于 OpenGL 的 GLSL、基于 DirectX 的 HLSL 等),这段代码的作用是阐述如何绘制每一个顶点的颜色以及最终每一个像素点的颜色。

顶点着色器 (Vertex Shader)

进行顶点相关的一系列操作。通常用来把顶点从模型空间变换到平面空间。

可以用于通过 矩阵变换位置、计算照明公式来 生成逐顶点颜色 以及 生成或变换纹理坐标 等基于顶点的操作。

几何着色器 (Geometry Shader)

以完整的图元作为输入数据,可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状,创建或销毁几何图元。

经过几何着色器处理后,得到的是一系列位于齐次裁剪空间的顶点所组成的图元,这些顶点会在后面的裁剪、透视除法和光栅化阶段得到进一步处理。

片元着色器 (Fragment Shader)

对片元进行纹理采样、颜色汇总等处理,计算一个像素的最终颜色。

GLSL

基本要求

Shader 的开头要声明版本,接着是输入和输出变量、uniformmain 函数。

输入和输出:使用关键字 inout 进行数据交流传递;顶点着色器应该为输入提供一个 layout 标识;片元着色器需要输出一个 vec4 的颜色。

uniform :一种从 CPU中的应用GPU 中的Shader 发送数据的方式;它是全局的;会一直保存数据,直到被重置或更新。我们也可以不采用 uniform ,而选择给 顶点属性设置 添加数据来设置颜色。

main :Shader 的入口,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。

顶点属性设置

将一个顶点属性设置为有:X、Y、Z、R、G、B 等数据;

1
2
3
4
5
6
float vertices[] = {
// 位置 // 颜色
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 顶部
};

设置一下 顶点着色器 的输入:

1
2
layout (location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0 
layout (location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1

image-20211004153906902

重新配置顶点属性指针,根据上图与设置可以发现:

  1. 位置属性 location = 0,两个顶点属性间隔为 6 * sizeof(float) ,起始偏移量为 0
  2. 颜色属性 location = 1,两个顶点属性间隔为 6 * sizeof(float) ,起始偏移量为 3 * sizeof(float)
1
2
3
4
5
6
7
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float)));
glEnableVertexAttribArray(1);

完整代码

image-20211004164014959

Shader.h (封装 Shader 的一些操作)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#ifndef SHADER_H
#define SHADER_H

#include <GL/glew.h>
#include <GL/GL.h>
#include <GLFW/glfw3.h>

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

class Shader
{
public:
unsigned int ID;

Shader(const char* vertexPath, const char* fragmentPath)
{
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;

vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);

try
{
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;

vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();

vShaderFile.close();
fShaderFile.close();

vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure& e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();


unsigned int vertex, fragment;

vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");

fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");

ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");

glDeleteShader(vertex);
glDeleteShader(fragment);
}

void use()
{
glUseProgram(ID);
}

void setBool(const std::string& name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}

void setInt(const std::string& name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}

void setFloat(const std::string& name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}

void setVec2(const std::string& name, float x, float y) const
{
glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);
}

void setVec3(const std::string& name, float x, float y, float z) const
{
glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);
}

void setVec4(const std::string& name, float x, float y, float z, float w) const
{
glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w);
}

private:

void checkCompileErrors(unsigned int shader, std::string type)
{
int success;
char infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
#endif

Shader01.vs

1
2
3
4
5
6
7
8
9
10
11
12
13
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 vertexColor;

uniform vec3 posOffset;

void main()
{
gl_Position = vec4(aPos + posOffset, 1.0);
vertexColor = aColor;
}

Shader01.fs

1
2
3
4
5
6
7
8
9
10
11
#version 330 core
in vec3 vertexColor;

out vec4 FragColor;

uniform vec4 colorOffset;

void main()
{
FragColor = vec4(vertexColor, 1.0f) + colorOffset;
}

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#define GLEW_STATIC
#include <GL/glew.h>
#include <GL/GL.h>
#include <GLFW/glfw3.h>
#include <Shader.h>
#include <iostream>

#pragma region BaseSetting

static GLFWwindow* window;
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
const unsigned int MAX_COUNT = 800 * 600;

static void InitializeWindow()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Test", NULL, NULL);
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, [](GLFWwindow* window, int width, int height){ glViewport(0, 0, width, height); });


glewExperimental = GL_TRUE;
glewInit();
}

static void ProcessInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
#pragma endregion


#pragma region InitializeVertex

static float vertices[] = {
// positions // colors
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom left
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // top
};
static unsigned int VAO, VBO;

static void Initialize()
{
// Generate
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);

// Bind
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

}

#pragma endregion


void Render()
{
glClearColor(0.5, 0.5, 0.5, 1);
glClear(GL_COLOR_BUFFER_BIT);

float time = glfwGetTime();
static Shader shader01("Shader01.vs", "Shader01.fs");

shader01.setVec3("posOffset", sin(time) / 2.0, sin(time) / 2.0, 0.0f);
shader01.setVec4("colorOffset", sin(time) / 2.0, sin(time) / 2.0, sin(time) / 2.0, 0.0f);

shader01.use();

glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
}

int main()
{
InitializeWindow();

Initialize();

while (!glfwWindowShouldClose(window))
{
ProcessInput(window);

Render();

glfwSwapBuffers(window);
glfwPollEvents();
}

glfwTerminate();
return 0;
}